#include <iostream>
#include <string>
#include <cstring>
#include <memory>
#include <unistd.h>
#include <pthread.h>
#include <functional>

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <jsoncpp/json/json.h>

class InetAddr
{
public:
    InetAddr(std::string ip, uint16_t port)
        : _ip(ip), _port(port)
    {
        memset(&_psocket, 0, sizeof(_psocket));

        _psocket.sin_family = AF_INET;
        _psocket.sin_port = htons(port);
        inet_pton(AF_INET, _ip.c_str(), &_psocket.sin_addr);
    }

    InetAddr(sockaddr_in psocket)
    {
        _port = ntohs(psocket.sin_port);
        char ipstr[30];
        inet_ntop(AF_INET, &psocket.sin_addr, ipstr, sizeof(ipstr));
        _ip = ipstr;

        _psocket = psocket;
    }

    std::string getip()
    {
        return _ip;
    }

    uint16_t getport()
    {
        return _port;
    }

    sockaddr_in getsocket()
    {
        return _psocket;
    }

private:
    sockaddr_in _psocket;
    std::string _ip;
    uint16_t _port;
};

void testinet_addr()
{
    InetAddr ia1("127.0.0.1", 8888);
    sockaddr_in server = ia1.getsocket();
    InetAddr ia2(server);
    printf("ip: %s, port: %d\n", ia2.getip().c_str(), ia2.getport());
}
//  -------------------------------------------------------------------------------------------

const std::string psep = "\r\n";

// {} -> len\r\n{}\r\n
std::string Encode(const std::string &package)
{
    std::string ret = "";
    std::string len_str = std::to_string(package.size());

    ret += len_str;
    ret += psep;
    ret += package;
    ret += psep;

    return ret;
}

// len\r\n
// len\r\n{}\r\nlen\r
std::string Decode(std::string &package)
{
    size_t pos = package.find(psep);
    if (pos == std::string::npos)
        return "";

    std::string len_str = package.substr(0, pos);
    if (len_str.size() == 0)
        return "";

    size_t psep_size = psep.size();
    size_t len_size = len_str.size();
    size_t text_size = std::stoi(len_str);
    if (package.size() < len_size + 2 * psep_size + text_size)
        return "";

    std::string text = package.substr(len_size + psep_size, text_size);

    package.erase(0, len_size + 2 * psep_size + text_size);

    return text;
}

void testDecodeEncode()
{
    std::string package = "10\r\n{1782+230}\r\n10\r\n{1782+230}\r\n10\r\n{1782+230}\r\n10\r\n{1782+230}\r\n";
    while (true)
    {
        std::string text = Decode(package);
        if (text == "")
            break;
        std::cout << text << std::endl;
    }
}
///////////////////////////////////////////////////////////////////////////////////
class Request
{
public:
    Request() {}
    Request(int x, int y, char op)
        : _x(x), _y(y), _op(op)
    {
    }

    std::string serialize()
    {
        Json::Value root;
        root["x"] = _x;
        root["y"] = _y;
        root["op"] = _op;

        Json::StyledWriter writer;

        std::string seria = writer.write(root);

        return seria;
    }

    bool deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        if (!res)
            return false;

        _x = root["x"].asInt();
        _y = root["y"].asInt();
        _op = root["op"].asInt();

        return true;
    }

    void print()
    {
        std::cout << "_x: " << _x << ", _y: " << _y << ", _op: " << _op << std::endl;
    }

    int _x;
    int _y;
    char _op;
};

class Factory
{
public:
    std::shared_ptr<Request> BuildRequest()
    {
        int x = rand() % 100;
        int y = rand() % 100;

        int i = rand() % 4;
        std::string ops = "+-*/";

        return std::make_shared<Request>(x, y, ops[i]);
    }

private:
};

void *routine(void *)
{
    pthread_detach(pthread_self());
    Factory fac;

    std::shared_ptr<Request> req = fac.BuildRequest();

    std::string message = Encode(req->serialize());

    std::string package = Decode(message);

    req->deserialize(package);

    req->print();
    return nullptr;
}

void testRequest()
{

    pthread_t tid;
    pthread_create(&tid, nullptr, routine, nullptr);
    sleep(10);
}

class Response
{
public:
    Response(int res, int code)
        : _res(res), _code(code)
    {
    }

    std::string serialize()
    {
        Json::Value root;
        root["res"] = _res;
        root["code"] = _code;

        Json::StyledWriter writer;

        return writer.write(root);
    }

    bool deserialize(std::string &in)
    {
        Json::Value root;
        Json::Reader reader;
        bool res = reader.parse(in, root);
        if (!res)
            return false;

        _res = root["res"].asInt();
        _code = root["code"].asInt();

        return true;
    }

    void print()
    {
        std::cout << "_res: " << _res << ", _code: " << _code << std::endl;
    }

    int _res;
    int _code;
};

void testResponse()
{
    std::shared_ptr<Request> req = std::make_shared<Request>(1, 2, '+');
    std::cout << "After serialize: " << req->serialize();
}

class Calculate
{
public:
    std::shared_ptr<Response> Execute(std::shared_ptr<Request> req)
    {
        char op = req->_op;
        int res = 0;
        int code = 0;
        switch (op)
        {
        case '+':
            res = req->_x + req->_y;
            break;
        case '-':
            res = req->_x + req->_y;
            break;
        case '*':
            res = req->_x * req->_y;
            break;
        case '/':
            if (req->_y == 0)
            {
                code = 1;
                break;
            }
            res = req->_x / req->_y;
            break;
        }

        return std::make_shared<Response>(res, code);
    }
};

void testcal()
{
    std::shared_ptr<Request> req(new Request(1, 0, '/'));
    Calculate cal;
    std::shared_ptr<Response> res = cal.Execute(req);

    req->print();
    res->print();
}
using exec_t = std::function<std::shared_ptr<Response>(std::shared_ptr<Request>)>;

int main()
{
    testRequest();
    return 0;
}